#include <stdio.h>
#include <stdlib.h>

#define LENGTH(a) (sizeof a / sizeof a[0])

typedef union Func Func;
typedef struct Layout Layout;

#include "modules/status.h"

typedef enum {
    type_int,
    type_unsigned_int,
    type_long,
    type_long_double,
    type_char,
    type_char_ptr
} Type;

union Func {
    int          (*d)  ();
    unsigned int (*u)  ();
    long         (*ld) ();
    long double  (*Lf) ();
    char         (*c)  ();
    char *       (*s)  ();
};

struct Layout {
    const char * format;
    const int bufsize;
    Type type;
    Func func;
};

static int get_whole_buffer_size();
static int concatenate_whole_buffer(char *, Arg *);
static int append_string(char *, char *);
static void update(Arg *);
static void update_arg(Arg *);

// must be defined in config.h
static void set_status(char *);

#include "config.h"

int
size(const char * str)
{
    int size = 0;
    for (int i = 0; *(str + i); i++) size++;
    return size + 1;
}

int
get_whole_buffer_size()
{
    int size = 0;
    for (long unsigned int i = 0; i < LENGTH(layouts); i++) size += layouts[i].bufsize;
    return size;
}

int
concatenate_whole_buffer(char * buf, Arg * arg)
{
    for (long unsigned int i = 0; i < LENGTH(layouts); i++) {
        char temp_buf[layouts[i].bufsize];

        // call .func with .arg and embed it's return value in .format
        // idk if there is a better way, soooo
        if (layouts[i].type == type_int) {
            sprintf(
                temp_buf,
                layouts[i].format,
                layouts[i].func.d(arg)
            );
        } else if (layouts[i].type == type_unsigned_int) {
            sprintf(
                temp_buf,
                layouts[i].format,
                layouts[i].func.u(arg)
            );
        } else if (layouts[i].type == type_long) {
            sprintf(
                temp_buf,
                layouts[i].format,
                layouts[i].func.ld(arg)
            );
        } else if (layouts[i].type == type_long_double) {
            sprintf(
                temp_buf,
                layouts[i].format,
                layouts[i].func.Lf(arg)
            );
        } else if (layouts[i].type == type_char) {
            sprintf(
                temp_buf,
                layouts[i].format,
                layouts[i].func.c(arg)
            );
        } else if (layouts[i].type == type_char_ptr) {
            sprintf(
                temp_buf,
                layouts[i].format,
                layouts[i].func.s(arg)
            );
        }

        // append temp_buf to the end of buf
        append_string(buf, temp_buf);
    }

    return 1;
}

int
append_string(char * buf, char * new)
{
    int bufsize = 0;

    for (int i = 0; buf[i] != '\0'; i++) bufsize++;
    for (int i = 0; new[i] != '\0'; i++) {
        buf[bufsize] = new[i];
        bufsize++;
    }

    buf[bufsize] = '\0';

    return 0;
}

void
update(Arg * arg)
{
    update_arg(arg);

    char str[get_whole_buffer_size()];
    str[0] = '\0';

    concatenate_whole_buffer(str, arg);

    set_status(str);
}

int
main()
{
    Arg * arg = malloc(sizeof(Arg));

    while (1) update(arg);

    // unreachable
    free(arg);
    return 0;
}
